package com.cnap.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.cnap.db.entity.ApplicationPO;
import com.cnap.db.entity.ClusterInfoPO;
import com.cnap.db.entity.MicroServicePO;
import com.cnap.db.entity.StrategyPO;
import com.cnap.db.mapper.MicroServiceMapper;
import com.cnap.db.mapper.StrategyMapper;
import com.cnap.model.application.ApplicationLabel;
import com.cnap.model.cloud.scaling.ScalingPolicyDTO;
import com.cnap.model.cloud.scaling.UpdateScalingParam;
import com.cnap.model.common.ResultCode;
import com.cnap.model.exception.JsonException;
import com.cnap.model.schedule.PolicyParam;
import com.cnap.model.schedule.ScaleStrategyDTO;
import com.cnap.model.schedule.ScaleStrategyTypeEnum;
import com.cnap.model.schedule.StrategyDTO;
import com.cnap.model.schedule.StrategyTypeEnum;
import com.cnap.model.schedule.engine.CommentResultForEngine;
import com.cnap.model.schedule.engine.DeployPolicyCalcRes;
import com.cnap.model.schedule.engine.DeployPolicyRequest;
import com.cnap.model.schedule.engine.EngineClusterInfo;
import com.cnap.model.schedule.engine.alg.AlgDeployClusterInfo;
import com.cnap.service.AlgorithmEngineService;
import com.cnap.service.ApplicationService;
import com.cnap.service.ClusterInfoService;
import com.cnap.service.MicroSvcService;
import com.cnap.service.ScheduleService;
import com.cnap.utils.JsonUtil;
import com.cnap.utils.TokenUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * <类文件描述>
 *
 * @author xc
 **/
@Service
public class ScheduleServiceImpl implements ScheduleService {
    private static final Logger LOGGER = LoggerFactory.getLogger(ScheduleServiceImpl.class);

    // 需如果存在部署策略，则也要更新部署策略的集群信息
    // 集群信息单独保存，所以此处不需要再更新部署策略的集群了

    // 单独更新集群信息
    // 集群信息单独拉出来保存
    // 需要改造策略中保存的集群信息
    // 统一由集群信息服务类提供集群信息查询管理

    // mapper
    private final MicroServiceMapper microServiceMapper;
    // mapper
    private final StrategyMapper strategyMapper;
    // service
    private final AlgorithmEngineService algorithmEngineService;
    // service
    private final ApplicationService applicationService;
    // service
    private final ClusterInfoService clusterInfoService;

    @Autowired
    public ScheduleServiceImpl(MicroServiceMapper microServiceMapper, StrategyMapper strategyMapper, AlgorithmEngineService algorithmEngineService, ApplicationService applicationService, ClusterInfoService clusterInfoService) {
        this.microServiceMapper = microServiceMapper;
        this.strategyMapper = strategyMapper;
        this.algorithmEngineService = algorithmEngineService;
        this.applicationService = applicationService;
        this.clusterInfoService = clusterInfoService;
    }

    /**
     * 计算部署策略
     *
     * @param strategyDTOs 请求
     */
    @Override
    @Transactional
    public void addScheduleStrategy(List<StrategyDTO> strategyDTOs) {
        for (StrategyDTO strategyDTO : strategyDTOs) {
            // 组装请求
            DeployPolicyRequest request = strategyDTO.toDeployPolicyRequest();
            MicroServicePO microServicePO = microServiceMapper.selectMicroServiceById(strategyDTO.getServiceId());
            // 判断权限
            if (!microServicePO.getUserName().equals(TokenUtils.getUserName())) {
                LOGGER.error("user {} has no permissions to add schedule strategy to microservice {}",
                        TokenUtils.getUserName(), microServicePO.getId());
                throw new JsonException(ResultCode.FORBIDDEN);
            }

            request.setYaml(microServicePO.getYaml());

            request.setAppCategory(microServicePO.getCategory());
            request.setUserName(TokenUtils.getUserName());
            // 设置二级标签
            // 算法需要使用
            setTags(microServicePO, request);

            CommentResultForEngine<DeployPolicyCalcRes> result = algorithmEngineService.calculateDeployPolicy(request);
            // 1. 解析
            DeployPolicyCalcRes res = result.getData();
            List<AlgDeployClusterInfo> algDeployClusterInfos = res.getAlgDeployClusterInfos();

            List<EngineClusterInfo> clusterInfos =
                    algDeployClusterInfos.stream().map(AlgDeployClusterInfo::toEngineClusterInfo).collect(Collectors.toList());

            // 参数信息，综合算法会有实际采用的策略信息
            PolicyParam policyParam = res.toPolicyParam();

            StrategyPO oldScheduleStrategy =
                    strategyMapper.selectStrategyByServiceIdAndType(strategyDTO.getServiceId(), StrategyTypeEnum.SCHEDULE_STRATEGY.getValue());

            if (Objects.nonNull(oldScheduleStrategy)) {
                // 如果存在旧策略
                // 更新策略
                LOGGER.warn("service({}) has added schedule strategy, try to update", strategyDTO.getServiceId());
                prepareStrategy(strategyDTO, oldScheduleStrategy, policyParam);
                strategyMapper.updateStrategy(oldScheduleStrategy);
            } else {
                // 2. 保存调度策略
                StrategyPO scheduleStrategy = new StrategyPO();
                scheduleStrategy.setId(IdUtil.simpleUUID());
                prepareStrategy(strategyDTO, scheduleStrategy, policyParam);
                strategyMapper.insertStrategy(scheduleStrategy);
            }
            // 需如果存在部署策略，则也要更新部署策略的集群信息
            // 集群信息单独保存，所以此处不需要再更新部署策略的集群了

            // 单独更新集群信息
            // 集群信息单独拉出来保存
            // 需要改造策略中保存的集群信息
            // 统一由集群信息服务类提供集群信息查询管理
            ClusterInfoPO clusterInfoPO = new ClusterInfoPO();
            // 算法推荐
            clusterInfoPO.setRecommendCloud(res.getRecommendCloud());
            // 用户选择
            clusterInfoPO.setUserChooseCloud(res.getUserChooseCloud());
            // id
            clusterInfoPO.setId(strategyDTO.getServiceId());
            // namespace
            clusterInfoPO.setNamespace(microServicePO.getNamespace());
            // appId
            clusterInfoPO.setAppId(microServicePO.getApplicationId());
            // clusterInfo
            clusterInfoPO.setClusterInfos(clusterInfos);
            // algDeploy
            clusterInfoPO.setAlgDeployClusterInfos(algDeployClusterInfos);
            // 隔离
            clusterInfoPO.setForcedIsolation(strategyDTO.isForcedIsolation());
            clusterInfoService.saveOrUpdateClusterInfo(microServicePO, clusterInfoPO);
        }
    }


    private void setTags(MicroServicePO microServicePO, DeployPolicyRequest request) {
        request.setUserName(TokenUtils.getUserName());
        // 查询标签
        ApplicationPO applicationPO = applicationService.selectByAppId(microServicePO.getApplicationId());
        ApplicationLabel labels = applicationPO.getLabel();
        // 场景
        request.setSceneTags(labels.getObjectScene());
        // 技术
        request.setTechnologyTags(labels.getTechnology());
    }

    private void prepareStrategy(StrategyDTO strategyDTO, StrategyPO scheduleStrategy, PolicyParam policyParam) {
        // 服务id
        scheduleStrategy.setServiceId(strategyDTO.getServiceId());
        // 策略类型
        scheduleStrategy.setType(StrategyTypeEnum.SCHEDULE_STRATEGY.getValue());
        // 策略名称
        scheduleStrategy.setStrategyName(strategyDTO.getStrategyName());
        // 策略参数
        scheduleStrategy.setStrategyParam(strategyDTO.getParams());
        // 是否使用默认参数
        scheduleStrategy.setEnableDefault(strategyDTO.getEnableDefault());
        // 参数
        scheduleStrategy.setStrategyParam(JsonUtil.objectToJsonStr(policyParam));
    }

    /**
     * 添加扩缩容策略
     *
     * @param strategyDTOS 策略
     */
    @Override
    public void addScaleStrategy(List<ScaleStrategyDTO> strategyDTOS) {
        /*
         添加扩缩容策略
         后台->算法引擎->算法
         */
        for (ScaleStrategyDTO strategyDTO : strategyDTOS) {
            StrategyPO scheduleStrategy = strategyMapper.selectStrategyByServiceIdAndType(strategyDTO.getServiceId(),
                    StrategyTypeEnum.SCHEDULE_STRATEGY.getValue());
            // 扩缩容策略依赖调度策略
            // 需要调度策略先配置好
            if (Objects.isNull(scheduleStrategy)) {
                LOGGER.error("can't find schedule strategy for service, id={}", strategyDTO.getServiceId());
                throw new JsonException(500, "请先进行调度策略配置");
            }

            MicroServicePO microServicePO = microServiceMapper.selectMicroServiceById(scheduleStrategy.getServiceId());
            // 判断权限
            if (!microServicePO.getUserName().equals(TokenUtils.getUserName())) {
                LOGGER.error("user {} has no permissions to add schedule strategy to microservice {}",
                        TokenUtils.getUserName(), microServicePO.getId());
                throw new JsonException(ResultCode.FORBIDDEN);
            }

            List<StrategyPO> oldScaleStrategys = strategyMapper.selectScaleStrategysByServiceId(strategyDTO.getServiceId());
            if (Objects.nonNull(oldScaleStrategys)) {
                LOGGER.warn("service({}) has added scale strategy, delete first and insert", strategyDTO.getServiceId());
                strategyMapper.deleteStrategyBySvcIdAndType(strategyDTO.getServiceId(), StrategyTypeEnum.SCALE_STRATEGY.getValue());
            }

            for (StrategyDTO dto : strategyDTO.getStrategys()) {
                // 插入
                StrategyPO scaleStrategy = new StrategyPO();
                // id
                scaleStrategy.setId(IdUtil.simpleUUID());
                // 服务id
                scaleStrategy.setServiceId(strategyDTO.getServiceId());
                // 策略类型
                scaleStrategy.setType(StrategyTypeEnum.SCALE_STRATEGY.getValue());
                // 名称
                scaleStrategy.setStrategyName(dto.getStrategyName());
                // 参数
                scaleStrategy.setStrategyParam(dto.getParams());
                // 是否使用默认
                scaleStrategy.setEnableDefault(dto.getEnableDefault());
                // 集群信息
                scaleStrategy.setClusterInfos(scheduleStrategy.getClusterInfos());
                // 扩缩容策略类型
                scaleStrategy.setScaleType(dto.getScaleType());
                // 插入
                strategyMapper.insertStrategy(scaleStrategy);
            }
        }
    }

    /**
     * 根据id删除策略
     *
     * @param strategyId 策略id
     * @return 结果
     */
    @Override
    public boolean deleteStrategyById(String strategyId) {
        // 判断
        StrategyPO strategyPO = strategyMapper.selectStrategyById(strategyId);
        if (Objects.isNull(strategyPO)) {
            LOGGER.error("can't find strategy by id={}", strategyId);
            throw new JsonException(500, "未找到该策略");
        }
        // 查询微服务信息
        MicroServicePO microServicePO = microServiceMapper.selectMicroServiceById(strategyPO.getServiceId());
        if (Objects.isNull(microServicePO)) {
            LOGGER.error("strategy{} has no microservice", strategyId);
            throw new JsonException(500, "数据错误");
        }
        // 判断权限
        if (!microServicePO.getUserName().equals(TokenUtils.getUserName())) {
            LOGGER.error("user {} has no permissions to delete strategy {}", TokenUtils.getUserName(), strategyId);
            throw new JsonException(ResultCode.FORBIDDEN);
        }

        return strategyMapper.deleteStrategyById(strategyId) == 1;
    }

    @Override
    @Transactional
    public boolean deleteStrategyByIds(List<String> strategyIds) {
        boolean result = true;
        for (String strategyId : strategyIds) {
            result = result && deleteStrategyById(strategyId);
        }
        return result;
    }

}
